
package vv.lines.core;

import vv.lines.core.context.IContext;
import vv.lines.view.FieldView;
import vv.lines.view.View;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

import static vv.lines.utils.LinesUtils.isValidMoving;

/**
 *
 * @author vpotapenko
 */
public class Field extends VisualObject implements IActionHandler {

    private CellController cellController;
    private BallController ballController;

    private boolean started;
    private IFieldListener fieldListener;

    public Field(IFieldListener fieldListener, IContext context) {
        super(context);
        this.fieldListener = fieldListener;

        initCellsObject();
        initBallsObject();
    }

    @Override
    protected View createView() {
        return getViewFactory().createFieldView(this);
    }

    public void handleAction(ActionId type) {
        switch (type) {
            case RESIZE:
                dispatchResize();
                break;
        }
    }

    public void dispatchNewPreviewBalls(List<Ball> balls) {
        getContext().setNextBalls(balls);
        fieldListener.nextBallsChanged();
    }

    public void start() {
        if (started) {
            cellController.clearAll();
            ballController.clearAll();

            dispatchResize();
        }

        started = true;
        step();
    }

    public void updateScore(int deleted) {
        getContext().addScore(deleted);
        fieldListener.scoreChanged();
    }

    public void dispatchBallSelection(Ball ball) {
        if (ballController.getSelectedBall() != ball) ballController.selectBall(ball);
        else ballController.clearSelection();
    }

    public void dispatchCellSelection(Cell target) {
        Ball ball = ballController.getSelectedBall();
        if (ball == null) return;

        Cell source = ball.getOwner();
        if (!isValidMoving(source, target, ball) || !moveBall(target, source, ball)) return;

        if (!checkColorArea(Collections.singleton(target))) step();
    }

    private boolean moveBall(Cell target, Cell source, Ball ball) {
        List<Cell> path = cellController.getPath(source, target);
        if (path.isEmpty()) return false;

        ballController.moveBall(ball, path);
        return true;
    }

    private boolean checkColorArea(Collection<Cell> lastActive) {
        Collection<Cell> colorArea = cellController.getColorArea(lastActive);
        ballController.removeBallFromCells(colorArea);
        cellController.clearArea(colorArea);

        return !colorArea.isEmpty();
    }

    private void initBallsObject() {
        ballController = new BallController(this);
    }

    private void initCellsObject() {
        cellController = new CellController(this);
        cellController.updateCellsBounds(getFieldWidth(), getFieldHeight());
    }

    private void gameLost() {
        fieldListener.gameLost();
    }

    private void step() {
        Collection<Cell> freeCells = cellController.getRandomCells();
        for (Cell cell : freeCells) {
            ballController.setGeneratedBallOnCell(cell);
        }
        ballController.flushGeneratedBalls();

        checkColorArea(freeCells);

        if (!cellController.hasFreeCells()) {
            gameLost();
        }
        getView().repaint();
    }

    @Override
    public FieldView getView() {
        return (FieldView) super.getView();
    }

    private void dispatchResize() {
        cellController.updateCellsBounds(getFieldWidth(), getFieldHeight());
        ballController.updateBounds();
    }

    private int getFieldWidth() {
        return getView().getFieldWidth();
    }

    private int getFieldHeight() {
        return getView().getFieldHeight();
    }
}
